package fr.babos.graphic.components.base 
{
	import flash.display.DisplayObjectContainer;
	import flash.events.Event;
	import flash.geom.Matrix;
	import org.osflash.signals.Signal;
	
	/**
	 * Composant de base avec gestion des dimensions.
	 * @author ZoulouX
	 */
	public class ResizableComponent extends BaseComponent
	{
		/**
		 * La largeur du composant
		 */
		protected var _localWidth				:Number						= 0;
		
		/**
		 * La hauteur du composant
		 */
		protected var _localHeight				:Number						= 0;
		
		/**
		 * Le signal du redimensionnement
		 */
		protected var _onResized				:Signal						= new Signal();
		
		/**
		 * Le parent écouté
		 */
		protected var _watchedParent			:ResizableComponent;
		
		/**
		 * Position absolue par rapport au haut
		 */
		protected var _top						:Number;
		
		/**
		 * Position absolue par rapport à la droite
		 */
		protected var _right					:Number;
		
		/**
		 * Position absolue par rapport au bas
		 */
		protected var _bottom					:Number;
		
		/**
		 * Position absolue par rapport à la gauche
		 */
		protected var _left						:Number;
		
		/**
		 * Centrage horizontal du composant
		 */
		protected var _horizontalCenter			:Number;
		
		/**
		 * Centrage vertical du composant
		 */
		protected var _verticalCenter			:Number;
		
		/**
		 * Le décalage de placement horizontal
		 */
		protected var _horizontalOffset			:Number						= 0;
		
		/**
		 * Le décalage de placement vertical
		 */
		protected var _verticalOffset			:Number						= 0;
		
		
		/**
		 * La largeur en passant par la superclasse
		 */
		public function get superWidth ():Number { return super.width; };
		public function set superWidth (value:Number):void
		{
			super.width = value;
		}
		
		/**
		 * La hauter en passant par la superclasse
		 */
		public function get superHeight ():Number { return super.height; };
		public function set superHeight (value:Number):void
		{
			super.height = value;
		}
		
		
		/**
		 * La largeur du composant
		 */
		override public function get width ():Number { return _localWidth; }
		override public function set width (value:Number):void
		{
			if ((value <= 0 || value > 0) && value != _localWidth)
			{
				// Enregistrer la nouvelle largeur
				_localWidth = value;
				
				// Actualiser la position
				needReplace(true);
			}
		}
		
		/**
		 * La hauteur du composant
		 */
		override public function get height ():Number { return _localHeight; }
		override public function set height (value:Number):void
		{
			if ((value <= 0 || value > 0) && value != _localHeight)
			{
				// Enregistrer la nouvelle largeur
				_localHeight = value;
				
				// Actualiser la position
				needReplace(true);
			}
		}
		
		
		/**
		 * Position absolue par rapport au haut
		 */
		public function get top ():Number { return _top; }
		public function set top (value:Number):void
		{
			// Enregistrer
			_top = value;
			
			// Actualiser la position
			needReplace();
		}
		
		/**
		 * Position absolue par rapport à la droite
		 */
		public function get right ():Number { return _right; }
		public function set right (value:Number):void
		{
			// Enregistrer
			_right = value;
			
			// Actualiser la position
			needReplace();
		}
		
		/**
		 * Position absolue par rapport au bas
		 */
		public function get bottom ():Number { return _bottom; }
		public function set bottom (value:Number):void
		{
			// Enregistrer
			_bottom = value;
			
			// Actualiser la position
			needReplace();
		}
		
		/**
		 * Position absolue par rapport à la gauche
		 */
		public function get left ():Number { return _left; }
		public function set left (value:Number):void
		{
			// Enregistrer
			_left = value;
			
			// Actualiser la position
			needReplace();
		}
		
		/**
		 * Centrage horizontal du composant
		 */
		public function get horizontalCenter ():Number { return _horizontalCenter; }
		public function set horizontalCenter (value:Number):void
		{
			// Enregistrer
			_horizontalCenter = value;
			
			// Actualiser la position
			needReplace();
		}
		
		/**
		 * Centrage vertical du composant
		 */
		public function get verticalCenter ():Number { return _verticalCenter; }
		public function set verticalCenter (value:Number):void
		{
			// Enregistrer
			_verticalCenter = value;
			
			// Actualiser la position
			needReplace();
		}
		
		/**
		 * Position horizontale
		 */
		override public function get x ():Number { return super.x; }
		override public function set x (value:Number):void
		{
			// Appliquer
			super.x = value;
			
			// Actualiser la position
			replaced();
		}
		
		/**
		 * Position verticale
		 */
		override public function get y ():Number { return super.y; }
		override public function set y (value:Number):void
		{
			// Appliquer
			super.y = value;
			
			// Actualiser la position
			replaced();
		}
		
		
		/**
		 * Signal appelé au redimensionnement
		 */
		public function get onResized ():Signal
		{
			return _onResized;
		}
		
		/**
		 * Le décalage de placement horizontal
		 */
		public function get horizontalOffset ():Number { return _horizontalOffset; }
		public function set horizontalOffset (value:Number):void 
		{
			// Enregistrer
			_horizontalOffset = value;
			
			// Actualiser la position
			needReplace();
		}
		
		/**
		 * Le décalage de placement vertical
		 */
		public function get verticalOffset ():Number { return _verticalOffset; }
		public function set verticalOffset (value:Number):void 
		{
			// Enregistrer
			_verticalOffset = value;
			
			// Actualiser la position
			needReplace();
		}
		
		
		/**
		 * Constructeur du composant avec gestion des dimensions
		 */
		public function ResizableComponent ()
		{
			
		}
		
		
		/**
		 * Initialisation du composant
		 */
		override protected function addedHandler (event:Event = null):void
		{
			// Si on a un parent redimensionnable
			if (parent != null && parent is ResizableComponent)
			{
				// Supprimer l'ancien si besoin
				if (_watchedParent != null)
					_watchedParent.onResized.remove(needReplace);
				
				// On le mémorise
				_watchedParent = (parent as ResizableComponent);
				
				// On écoute les changements de taille
				_watchedParent.onResized.add(needReplace);
				
				// Replacer
				needReplace();
			}
			
			// Relayer
			super.addedHandler(event);
		}
		
		/**
		 * Définir la taille du composant
		 * @param	pWidth : La largeur
		 * @param	pHeight : La hauteur
		 * @return this, méthode chaînable
		 */
		public function size (pWidth:Number, pHeight:Number):ResizableComponent
		{
			// Si c'est différent
			if (pWidth != _localWidth || pHeight != _localHeight)
			{
				// Enregistrer les nouvelles dimensions
				_localWidth = pWidth;
				_localHeight = pHeight;
				
				// Dispatcher
				_onResized.dispatch();
				
				// Signaler
				resized();
			}
			
			// Méthode chaînable
			return this;
		}
		
		/**
		 * Placer le composant. Permet de configurer toutes les variables de placement en redispatchant qu'un seul signal.
		 * @param	pTop : Le décallage depuis le haut
		 * @param	pRight : Le décallage depuis la droite
		 * @param	pBottom : Le décallage depuis le bas
		 * @param	pLeft : Le décallage depuis la gauche
		 * @return this, méthode chaînable
		 */
		public function place (pTop:Number = NaN, pRight:Number = NaN, pBottom:Number = NaN, pLeft:Number = NaN):ResizableComponent
		{
			// Tout enregistrer
			_top = pTop;
			_right = pRight;
			_bottom = pBottom;
			_left = pLeft;
			
			// Redispatcher
			needReplace();
			
			// Méthode chaînable
			return this;
		}
		
		/**
		 * Centrer l'élément
		 * @param	pHorizontalCenter : Le décallage par rapport au centre horizontal
		 * @param	pVerticalCenter : Le décallage par rapport au centre vertical
		 * @return this, méthode chaînable
		 */
		public function center (pHorizontalCenter:Number = NaN, pVerticalCenter:Number = NaN):ResizableComponent
		{
			// Enregistrer
			_horizontalCenter = pHorizontalCenter;
			_verticalCenter = pVerticalCenter;
			
			// Redispatcher
			needReplace();
			
			// Méthode chaînable
			return this;
		}
		
		/**
		 * Spécifier le parent de ce composant
		 * @param	pParent : Le parent auquel ajouter ce composant
		 * @param	pName : Le nom de ce composant
		 * @param	pAt : A quel niveau (-1 pour au dessus)
		 * @return this, méthode chaînable
		 */
		public function into (pParent:DisplayObjectContainer, pName:String = "", pAt:int = -1):ResizableComponent
		{
			// Définir le nom si besoin
			if (pName != "")
				name = pName;
			
			// Ajouter au parent au bon étage
			if (pAt == -1)
				pParent.addChild(this);
			else
				pParent.addChildAt(this, pAt);
			
			// Méthode chaînable
			return this;
		}
		
		/**
		 * Le composant a besoin d'être replacé
		 */
		protected function needReplace (pNeedResized:Boolean = false):void
		{
			// Si on a un parent
			if (_watchedParent != null)
			{
				// L'ancienne valeur pour vérifier le changement
				var oldValue:Number;
				
				// Centrer horizontalement ou placer
				if (_horizontalCenter <= 0 || _horizontalCenter > 0)
				{
					// Centrer
					x = _watchedParent.width / 2 - _localWidth / 2 + _horizontalCenter + _horizontalOffset;
				}
				else
				{
					// Alignement sur la gauche
					if (_left <= 0 || _left > 0)
					{
						// Placer à gauche
						x = _left + _horizontalOffset;
					}
					
					// Alignement sur la droite
					if (!(_left <= 0 || _left > 0) && (_right <= 0 || _right > 0))
					{
						// Placer à droite
						x = _watchedParent.width - _localWidth - _right + _horizontalOffset;
					}
					else if ((_left <= 0 || _left > 0) && (_right <= 0 || _right > 0))
					{
						// Enregistrer l'ancienne valeur
						oldValue = _localWidth;
						
						// Largeur fluide
						_localWidth = Math.max(0, _watchedParent.width - _left - _right);
						
						// Redimensionner
						if (_localWidth != oldValue)
							pNeedResized = true;
					}
				}
				
				// Centrer verticalement ou placer
				if (_verticalCenter <= 0 || _verticalCenter > 0)
				{
					// Centrer
					y = _watchedParent.height / 2 - _localHeight / 2 + _verticalCenter + _verticalOffset;
				}
				else
				{
					// Alignement en haut
					if (_top <= 0 || _top > 0)
					{
						// Placer en haut
						y = _top + _verticalOffset;
					}
					
					// Alignement en bas
					if (!(_top <= 0 || _top > 0) && (_bottom <= 0 || _bottom > 0))
					{
						// Placer en base
						y = _watchedParent.height - _localHeight - _bottom + _verticalOffset;
					}
					else if ((_top <= 0 || _top > 0) && (_bottom <= 0 || _bottom > 0))
					{
						// Enregistrer l'ancienne valeur
						oldValue = _localHeight;
						
						// Hauteur fluide
						_localHeight = Math.max(0, _watchedParent.height - _top - _bottom);
						
						// Redimensionner
						if (_localHeight != oldValue)
							pNeedResized = true;
					}
				}
			}
			
			// Si on a touché aux dimensions du composant
			if (pNeedResized)
			{
				// Dispatcher
				_onResized.dispatch();
				
				// Signaler
				resized();
			}
		}
		
		/**
		 * Si on doit placer ce composant en bitmap
		 */
		public function cab (pCacheAsBitmapEnabled:Boolean, pMatrix:Matrix = null):ResizableComponent
		{
			cacheAsBitmap = pCacheAsBitmapEnabled;
			cacheAsBitmapMatrix = pMatrix;
			
			return this;
		}
		
		/**
		 * Le composant est redimentionné
		 */
		protected function resized ():void
		{
			
		}
		
		/**
		 * Le composant est replacé
		 */
		protected function replaced ():void
		{
			
		}
		
		/**
		 * Destruction du composant
		 */
		override protected function removedHandler (event:Event):void
		{
			// Relayer
			super.removedHandler(event);
			
			// Supprimer tous les listeners
			_onResized.removeAll();
			
			// On n'écoute les changements de taille
			if (_watchedParent != null)
			{
				_watchedParent.onResized.remove(needReplace);
				_watchedParent = null;
			}
		}
	}
}